﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MTP
{
    

    class Reversi
    {
        static int[] dx = { 0, 1, 1, 1, 0, -1, -1, -1 };
        static int[] dy = { 1, 1, 0, -1, -1, -1, 0, 1 };

        static int PROF_MAX = 7;
        static bool USAR_ALPHA_BETA = true;
        static int pasos;
       
        private EstadoReversi t;
        private int jugador;

        private ReversiForm _formulario;
        private ParametrosReversi _parametros;

        public Reversi(ReversiForm formulario, ParametrosReversi parametros)
        {
            // Registramos el formulario
            _formulario = formulario;
            _parametros = parametros;

            if (_parametros.Dificultad == Dificultad.FACIL)
                PROF_MAX = 1;
            else if (_parametros.Dificultad == Dificultad.MEDIO)
                PROF_MAX = 5;
            else if (_parametros.Dificultad == Dificultad.MEDIO)
                PROF_MAX = 9;

            // Registramos las opciones de partida
            
            // El resto de inicializaciones
            int n = 8; // Tamaño del tablero.
            t = new EstadoReversi(n, 0);
            jugador = EstadoReversi.NEGRO;
        }

        public void empezar_juego()
        {
            //int n = 8; // Tamaño del tablero.
            //EstadoReversi t = new EstadoReversi(n, 0);
           

            //int jugador = EstadoReversi.NEGRO;

            escribir_tablero(t);
            escribir_estadisticas();

            while (!finPartida(t))
            {

                if (jugador == EstadoReversi.BLANCO)
                    return; // if(!esjugar_blanco(t);
                if (jugador == EstadoReversi.NEGRO)
                    jugar_negro(t);
                jugador = adv(jugador);

                escribir_tablero(t);
            }

            escribir_ganador(t);
            escribir_tablero(t);
            escribir_estadisticas();
        }

        public void jugada()
        {

            escribir_tablero(t);
            escribir_estadisticas();

            while (!finPartida(t))
            {

                if (jugador == EstadoReversi.BLANCO)
                {
                    if (_parametros.blancaEsIA())
                        jugar_blanco(t);//return; // if(_negroEsIA) jugar_blanco(t); else return;
                    else
                        return;

                }
                if (jugador == EstadoReversi.NEGRO)
                {
                    if (_parametros.negraEsIA())
                        jugar_negro(t);
                    else
                        return;
                    
                }
                // Cambiamos el turno.
                jugador = adv(jugador);
                
                // Quizás si estamos ante un jugador humano, habría que evaluar si puede mover o no antes de devolver el control al usuario.
                return;
                //escribir_tablero(t);
                
            }

            escribir_ganador(t);
            escribir_tablero(t);
            escribir_estadisticas();
        }

        private void escribir_tablero(EstadoReversi t)
        {
            for (int i = 0; i < t.n; i++)
                for (int j = 0; j < t.n; j++)
                    _formulario.pintaCasilla(i, j, t.tablero[i, j]);
        }

        private void escribir_ganador(EstadoReversi t)
        {
            if(t.pt_b > t.pt_n)
                _formulario.pintaGanador(EstadoReversi.BLANCO);
            else if(t.pt_b < t.pt_n)
                _formulario.pintaGanador(EstadoReversi.NEGRO);
            else
                _formulario.pintaGanador(EstadoReversi.EMPATE);

        }

        private void escribir_estadisticas()
        {
            _formulario.pintaEstadisticas(t.pt_b, t.pt_n, jugador);
        }

        private void jugar_blanco(EstadoReversi t)
        {
            ResMinMaxReversi res;
            res = max_ab(t, PROF_MAX, Int32.MaxValue);
            if (res.i != -1)
                aplicarJugada(t, res.i, res.j, EstadoReversi.BLANCO);
        }

        public void jugar_humano(int i, int j) //throw Exception
        {
            if (jugadaValida(t, i, j, jugador))
            {
                aplicarJugada(t, i, j, jugador);
                jugador = adv(jugador);
                jugada(); // Relanzamos algoritmo.
            }
            //else
              //  throw Exception();
        }
        //------------------------------------------------------------
        
        private void jugar_negro(EstadoReversi t) 
        {
		    ResMinMaxReversi res;
		    res = min_ab(t,PROF_MAX,Int32.MinValue);
		    //System.out.println(res.i+" "+res.j);
		    if ( res.i != -1 )
			    aplicarJugada(t, res.i, res.j, EstadoReversi.NEGRO);		
		
	    }


        private ResMinMaxReversi max_ab(EstadoReversi t, int prof, int beta)
        {
            pasos++;
            ResMinMaxReversi res = new ResMinMaxReversi();

            if (finPartida(t))
            {
                res.i = res.j = -1;
                int g = ganador(t);

                if (g == EstadoReversi.NEGRO)
                    res.v = -1 * (t.n * t.n) - 1;
                else if (g == EstadoReversi.BLANCO)
                    res.v = t.n * t.n + 1;
                else res.v = 0;

                return res;
            }

            if (prof == 0)
            {
                res.i = res.j = -1;
                res.v = eval(t);
                return res;
            }

            res.v = Int32.MinValue;  // res.v es beta
            res.i = res.j = -1;

            EstadoReversi tp;
            bool hayJugadaValida = false;
            int i = 0;
            int j = 0;
            while (i < t.n && (!USAR_ALPHA_BETA || res.v < beta))
            {
                j = 0;
                while (j < t.n && (!USAR_ALPHA_BETA || res.v < beta))
                {
                    if (jugadaValida(t, i, j, EstadoReversi.BLANCO))
                    {
                        hayJugadaValida = true;
                        tp = new EstadoReversi(t);
                        aplicarJugada(tp, i, j, EstadoReversi.BLANCO);
                        ResMinMaxReversi res_aux = min_ab(tp, prof - 1, res.v);
                        if (res_aux.v > res.v)
                        {
                            res.v = res_aux.v;
                            res.i = i;
                            res.j = j;
                        }
                    }
                    j++;
                }
                i++;
            }

            if (!hayJugadaValida)
            {
                tp = new EstadoReversi(t);
                ResMinMaxReversi res_aux = min_ab(tp, prof - 1, res.v);
                if (res_aux.v > res.v)
                {
                    res.v = res_aux.v;
                    res.i = -1;
                    res.j = -1;
                }
            }

            return res;
        }
        
        //-----------------------------------------------------------

        private ResMinMaxReversi min_ab(EstadoReversi t, int prof, int alpha)
        {
            pasos++;
            ResMinMaxReversi res = new ResMinMaxReversi();

            if (finPartida(t))
            {
                res.i = res.j = -1;
                int g = ganador(t);

                if (g == EstadoReversi.NEGRO)
                    res.v = -1 * (t.n * t.n) - 1;
                else if (g == EstadoReversi.BLANCO)
                    res.v = t.n * t.n + 1;
                else res.v = 0;

                return res;
            }

            if (prof == 0)
            {
                res.i = res.j - 1;
                res.v = eval(t);
                return res;
            }

            res.v = Int32.MaxValue;  // res.v es beta
            res.i = res.j = -1;

            EstadoReversi tp;
            bool hayJugadaValida = false;
            int i = 0;
            int j = 0;
            while (i < t.n && (!USAR_ALPHA_BETA || res.v > alpha))
            {
                j = 0;
                while (j < t.n && (!USAR_ALPHA_BETA || res.v > alpha))
                {
                    if (jugadaValida(t, i, j, EstadoReversi.NEGRO))
                    {
                        hayJugadaValida = true;
                        tp = new EstadoReversi(t);
                        aplicarJugada(tp, i, j, EstadoReversi.NEGRO);
                        ResMinMaxReversi res_aux = max_ab(tp, prof - 1, res.v);
                        if (res_aux.v < res.v)
                        {
                            res.v = res_aux.v;
                            res.i = i;
                            res.j = j;
                        }
                    }
                    j++;
                }
                i++;
            }

            if (!hayJugadaValida)
            {
                tp = new EstadoReversi(t);
                ResMinMaxReversi res_aux = max_ab(tp, prof - 1, res.v);
                if (res_aux.v < res.v)
                {
                    res.v = res_aux.v;
                    res.i = -1;
                    res.j = -1;
                }

            }
            return res;
        }


        private int ganador(EstadoReversi t) 
        {
		    if ( t.pt_b > t.pt_n ) 
                return EstadoReversi.BLANCO;
		    else if ( t.pt_n > t.pt_b ) 
                return EstadoReversi.NEGRO;
		    else return EstadoReversi.EMPATE;
	    }

        /// <summary>
        /// Evaluación heurística?????
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
	    private int eval(EstadoReversi t) 
        {
            if (_parametros.Dificultad != Dificultad.DIFICIL)
                return t.pt_b - t.pt_n;
            else
            {
                int valor_blancas = t.pt_b;
                int valor_negras = t.pt_n;

                int ESQ = 8;
                int LAT = 3;

                valor_blancas = (t.tablero[0,0] == 0) ? ESQ : 0;
                valor_blancas = (t.tablero[0,7] == 0) ? ESQ : 0;
                valor_blancas = (t.tablero[7,0] == 0) ? ESQ : 0;
                valor_blancas = (t.tablero[7,7] == 0) ? ESQ : 0;

                valor_negras = (t.tablero[0, 0] == 1) ? ESQ : 0;
                valor_negras = (t.tablero[0, 7] == 1) ? ESQ : 0;
                valor_negras = (t.tablero[7, 0] == 1) ? ESQ : 0;
                valor_negras = (t.tablero[7, 7] == 1) ? ESQ : 0;

                for (int i = 0; i < 8; i++)
                {
                    valor_blancas = (t.tablero[i,0] == 0) ? LAT : 0;
                    valor_blancas = (t.tablero[i,7] == 0) ? LAT : 0;
                    valor_blancas = (t.tablero[0,i] == 0) ? LAT : 0;
                    valor_blancas = (t.tablero[7,i] == 0) ? LAT : 0;

                    valor_negras = (t.tablero[i,0] == 1) ? LAT : 0;
                    valor_negras = (t.tablero[i,7] == 1) ? LAT : 0;
                    valor_negras = (t.tablero[7,i] == 1) ? LAT : 0;
                    valor_negras = (t.tablero[7,i] == 1) ? LAT : 0;
                }

                return valor_blancas - valor_negras;
            }
	    }



	    private static bool finPartida(EstadoReversi t) 
        {

		    if ( t.pd_b == 0 && t.pd_n == 0 ) 
                return true;

		    for(int i=0; i<t.n; i++)
			    for(int j=0; j<t.n; j++) 
				    if ( t.tablero[i,j] == EstadoReversi.VACIO &&
				         ( ( t.pd_b>0 && jugadaValida(t,i,j,EstadoReversi.BLANCO) ) ||
				    	       ( t.pd_n>0 && jugadaValida(t,i,j,EstadoReversi.NEGRO) ) ) ) 
					    return false;
		    return true;	
	    }

	    private static bool jugadaValida(EstadoReversi t, int i, int j, int jugador) 
        {

		    if ( t.tablero[i,j] == EstadoReversi.VACIO ) 
            {
			    for(int k = 0; k < 8; k++) 
                {
				    int ni=i+dx[k];
				    int nj=j+dy[k];
				    
                    if ( ni>=0 && ni<t.n && nj>=0 && nj<t.n && t.tablero[ni,nj] == adv(jugador) ) 
                    {
					    while ( ni>=0 && ni<t.n && 
							    nj>=0 && nj<t.n && 
							    t.tablero[ni,nj]!=EstadoReversi.VACIO ) 
                        {
						    if ( t.tablero[ni,nj] == jugador ) 
                                return true;
						    ni = ni+dx[k];
						    nj = nj+dy[k];
					    }
				    }
			    }
		    }
		    return false;
	    }

	    private static void aplicarJugada(EstadoReversi t, int i, int j, int jugador) 
        {
		    anyadir_pieza(t,i,j,jugador);
		
		    int ni;
		    int nj;
		    for(int k=0; k<8; k++) 
            {
			    ni=i+dx[k];
			    nj=j+dy[k];

			    if ( ni>=0 && ni<t.n && nj>=0 && nj<t.n && t.tablero[ni,nj] == adv(jugador) ) 
                {
				    bool aplicable = false;
				    while ( ni>=0 && ni<t.n && 
						    nj>=0 && nj<t.n && 
						    t.tablero[ni,nj]!=EstadoReversi.VACIO && !aplicable ) 
                    {
					    if ( t.tablero[ni,nj] == jugador ) 
                            aplicable = true;
					    ni = ni+dx[k];
					    nj = nj+dy[k];
				    }

				    if ( aplicable ) {
					    ni = i+dx[k];
					    nj = j+dy[k];
					    while ( t.tablero[ni,nj] != jugador ) {
						    voltea_pieza(t,ni,nj);
						    ni = ni+dx[k];
						    nj = nj+dy[k];
					    }	
				    }
			    }
		    }
	    }

	    private static void voltea_pieza(EstadoReversi t, int i, int j) 
        {

		    if ( t.tablero[i,j] == EstadoReversi.BLANCO ) 
            {
			    t.tablero[i,j] = EstadoReversi.NEGRO;
			    t.pt_b--;
			    t.pt_n++;
		    } 
            else 
            {
			    t.tablero[i,j] = EstadoReversi.BLANCO;
			    t.pt_b++;
			    t.pt_n--;
		    }
		
	    }

	    private static void anyadir_pieza(EstadoReversi t, int i, int j, int jugador) 
        {
		    t.tablero[i,j] = jugador;
		    if ( jugador == EstadoReversi.BLANCO ) 
            {
			    t.pt_b++;
			    t.pd_b--;
		    } 
            else
            {
			    t.pt_n++;
			    t.pd_n--;
		    }
	    }

	    private static int adv(int jugador) 
        {
		    if ( jugador == EstadoReversi.BLANCO ) 
                return EstadoReversi.NEGRO;
		    else 
                return EstadoReversi.BLANCO;
	    }
        /*
	    public static void main(String[] args) 
        {
		    pasos=0;
		    reversi(8);
		    System.out.println("pasos="+pasos);
	    }*/
    }
}

